package org.dftproject.genesis.ui.figures.zoompan;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

import org.eclipse.draw2d.Cursors;
import org.eclipse.draw2d.FigureListener;
import org.eclipse.draw2d.FlowLayout;
import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.MouseEvent;
import org.eclipse.draw2d.MouseListener;
import org.eclipse.draw2d.MouseMotionListener;
import org.eclipse.draw2d.ScalableLayeredPane;
import org.eclipse.draw2d.ScrollPane;
import org.eclipse.draw2d.UpdateManager;
import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Point;

public class ZoomPanContainer extends ScrollPane {
	
	private final ScalableLayeredPane scalableFigure;

	private IFigure focus;
	private Point prevFocusCenter;
	private Point prevCenter;

	public ZoomPanContainer() {
		setScrollBarVisibility(ScrollPane.NEVER);

		setViewport(new CenteredViewport(true));

		// Keep the viewport centered; moved in the following listener includes
		// resizing, believe it or not

		getViewport().addPropertyChangeListener(CenteredViewport.CENTER_LOCATION, new PropertyChangeListener() {

			public void propertyChange(PropertyChangeEvent event) {
				prevCenter = (Point) event.getNewValue();
			}

		});
		addFigureListener(new FigureListener() {

			public void figureMoved(IFigure source) {
				if (prevCenter != null)
					scrollTo(prevCenter);
			}

		});
		
		setOpaque(true);
		setRequestFocusEnabled(true);
		addListeners();

		scalableFigure = new ScalableLayeredPaneEx();
		scalableFigure.setLayoutManager(new FlowLayout());
		setContents(scalableFigure);
	}

	protected void addListeners() {
		MouseTracker tracker = new MouseTracker();
		addMouseMotionListener(tracker);
		addMouseListener(tracker);
	}

	public void setScale(double scale) {
		scalableFigure.setScale(scale);
	}
	
	public double getScale() {
		return scalableFigure.getScale();
	}
	
	public Point getFigureLocation(IFigure figure) {
		return figure.getBounds().getCenter().translate(scalableFigure.getLocation().getNegated()).scale(scalableFigure.getScale());
	}

	@Override
	public void scrollTo(Point location) {
		getViewport().setViewCenter(location);
	}

	public void smoothScrollTo(Point location) {
		int x = location.x;
		int y = location.y;

		int oldX = getViewport().getViewCenter().x;
		int oldY = getViewport().getViewCenter().y;
		int dx = x - oldX;
		int dy = y - oldY;

		if (dx == 0 && dy == 0)
			return;

		UpdateManager updateManager = getViewport().getUpdateManager();
		updateManager.performValidation();
		float progress = 0.1f;
		long duration = 250;
		long startTime = System.currentTimeMillis();

		while (progress != 0) {
			scrollTo(new Point(oldX + (int) (progress * dx), oldY + (int) (progress * dy)));
			updateManager.performUpdate();
			if (progress == 1.0)
				progress = 0;
			else {
				int delta = (int) (System.currentTimeMillis() - startTime);
				if (delta >= duration)
					progress = 1f;
				else
					progress = 0.1f + 0.9f * delta / duration;
			}
		}
	}
	
	@Override
	public CenteredViewport getViewport() {
		return (CenteredViewport) super.getViewport();
	}

	public void setFocus(IFigure figure) {
		focus = figure;
		if (prevFocusCenter != null)
			prevFocusCenter = figure.getBounds().getCenter();
	}

	public void maintainFocus() {
		if (focus != null) {
			Point newLocation;
			if (prevFocusCenter == null) {
				newLocation = prevFocusCenter = getFigureLocation(focus);
			} else {
				Dimension offset = getViewport().getViewCenter().getDifference(prevFocusCenter);

				prevFocusCenter = getFigureLocation(focus);
				newLocation = prevFocusCenter.getTranslated(offset);
			}

			// The viewport scroll bars have not yet been adjusted as a
			// result of this layout; manually adjust them so that the
			// scroll doesn't fail

			getViewport().readjustScrollBars();
			scrollTo(newLocation);
		}
	}

	protected class MouseTracker extends MouseMotionListener.Stub implements MouseListener {

		private Point mouseLocation;
		private Point viewLocation;

		public void mousePressed(MouseEvent me) {
			requestFocus();
			me.consume();

			mouseLocation = me.getLocation();
			viewLocation = viewport.getViewLocation();
		}

		public void mouseReleased(MouseEvent me) {
			revalidate();
		}

		public void mouseDoubleClicked(MouseEvent me) {
		}

		@Override
		public void mouseEntered(MouseEvent me) {
			setCursor(Cursors.SIZEALL);
		}

		@Override
		public void mouseDragged(MouseEvent me) {
			viewport.setViewLocation(viewLocation.getTranslated(mouseLocation.getDifference(me.getLocation())));
		}

		@Override
		public void mouseExited(MouseEvent me) {
			setCursor(null);
		}

	}
	
}
